using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;
using Microsoft.Practices.Unity.Utility;

namespace UnityCommonServiceManagerAdapter.Configuration
{
	/// <summary>
	/// A class that holds the collection of information
	/// for a constructor, so that the container can
	/// be configured to call this constructor.
	/// </summary>
	internal class InjectionConstructorNamed : InjectionMember
	{
		private readonly IEnumerable<InjectionParameterValue> parameterValues;

		/// <summary>
		/// Create a new instance of <see cref="InjectionConstructor"/> that looks
		/// for a constructor with the given set of parameters.
		/// </summary>
		/// <param name="namedParameterValues">The named values for the parameters, that will
		/// be converted to <see cref="InjectionParameterValue"/> objects.</param>
		public InjectionConstructorNamed(params Tuple<Type, string>[] namedParameterValues)
		{
			parameterValues = namedParameterValues
				.Select(namedValue => new ResolvedParameter(namedValue.Item1, namedValue.Item2))
				.Cast<InjectionParameterValue>();
		}

		/// <summary>
		/// Add policies to the <paramref name="policies"/> to configure the
		/// container to call this constructor with the appropriate parameter values.
		/// </summary>
		/// <param name="serviceType">Interface registered, ignored in this implementation.</param>
		/// <param name="implementationType">Type to register.</param>
		/// <param name="name">Name used to resolve the type object.</param>
		/// <param name="policies">Policy list to add policies to.</param>
		public override void AddPolicies(Type serviceType, Type implementationType, string name, IPolicyList policies)
		{
			var ctor = FindConstructor(implementationType);

			policies.Set<IConstructorSelectorPolicy>(
					new SpecifiedConstructorSelectorPolicy(ctor, parameterValues.ToArray()),
					new NamedTypeBuildKey(implementationType, name));
		}

		private ConstructorInfo FindConstructor(Type typeToCreate)
		{
			var matcher = new ParameterMatcher(parameterValues);
			return typeToCreate.GetConstructors()
												 .Where(ctor => matcher.Matches(ctor.GetParameters()))
												 .SingleOrDefault();
			//foreach (var ctor in typeToCreate.GetConstructors()
			//                                 .Where(ctor => matcher.Matches(ctor.GetParameters())))
			//{
			//  return ctor;
			//}

			// Since we use type safe calls this can never happen! ;~)
			//var signature = string.Join(", ", parameterValues.Select(p => p.ParameterTypeName).ToArray());
			//var message = string.Format(CultureInfo.CurrentCulture, Strings.NoSuchConstructor, typeToCreate.FullName, signature);
			//throw new InvalidOperationException(message);
		}
	}
}